﻿using SqlSugar;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SqlSugar
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly ConcurrentDictionary<string, SqlSugarProvider> _transDict;
        private readonly ConcurrentStack<string> _transactionStack = new ConcurrentStack<string>();
        /// <summary>
        /// SqlSugar 对象
        /// </summary>
        private readonly SqlSugarClient _sqlSugarClient;

        public UnitOfWork(ISqlSugarClient sqlSugarClient)
        {
            _sqlSugarClient = (SqlSugarClient)sqlSugarClient;
            _transDict = new ConcurrentDictionary<string, SqlSugarProvider>();
            _transactionStack = new ConcurrentStack<string>();
        }

        public virtual void BeginTran(string configId, IsolationLevel isolationLevel)
        {
            if (!_transDict.TryGetValue(configId, out _))
            {
                _sqlSugarClient.ChangeDatabase(configId);
                _sqlSugarClient.Ado.BeginTran(isolationLevel);
                _transDict.TryAdd(configId, _sqlSugarClient.Context);
            }

            string token = Guid.NewGuid().ToString();
            _transactionStack.Push(token);
        }

        public virtual void CommitTran()
        {
            _transactionStack.TryPop(out _);
            if (!_transactionStack.IsEmpty) return;
            
            foreach (SqlSugarProvider provider in _transDict.Values)
            {
                try
                {
                    provider.Ado.CommitTran();
                }
                catch
                {
                    SugarRetry.Execute(delegate
                    {
                        provider.Ado.CommitTran();
                    }, new TimeSpan(0, 0, 5));
                }
            }
        }

        public virtual void RollbackTran()
        {
            foreach (SqlSugarProvider provider in _transDict.Values)
            {
                try
                {
                    provider.Ado.RollbackTran();
                }
                catch
                {
                    SugarRetry.Execute(delegate
                    {
                        provider.Ado.RollbackTran();
                    }, new TimeSpan(0, 0, 5));
                }
            }
        }

        public virtual void Dispose()
        {
            foreach (SqlSugarProvider provider in _transDict.Values)
            {
                provider.Ado.Dispose();
            }
        }
    }
}
